package classfile

/*
*
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;

	 {     u2 start_pc;
			u2 end_pc;
			u2 handler_pc;
			u2 catch_type;
	} exception_table[exception_table_length];

u2 attributes_count;
attribute_info attributes[attributes_count];
}
*/
type CodeAttr struct {
	AttrStruct
	maxStack             uint16
	maxLocals            uint16
	codeLength           uint32
	codeList             []uint8
	exceptionTableLength uint16
	exceptionTableList   []ExceptionTable
	attributeCount       uint16
	attributeInfoList    []AttributeInfo
}

type ExceptionTable struct {
	startPc   uint16
	endPc     uint16
	handlerPc uint16
	catchType uint16
}

func (self ExceptionTable) StartPc() uint16 {
	return self.startPc
}
func (self ExceptionTable) EndPc() uint16 {
	return self.startPc
}
func (self ExceptionTable) HandlerPc() uint16 {
	return self.handlerPc
}

func (self ExceptionTable) CatchType() uint16 {
	return self.catchType
}

func (self *CodeAttr) readAttr(cp ConstantPool) AttributeInfo {
	self.maxStack = self.attributeData.ReadUInt16()
	self.maxLocals = self.attributeData.ReadUInt16()
	self.codeLength = self.attributeData.ReadUInt32()
	self.codeList = self.attributeData.ReadBytes(self.codeLength)
	self.exceptionTableLength = self.attributeData.ReadUInt16()
	if self.exceptionTableLength > 0 {
		self.exceptionTableList = make([]ExceptionTable, self.exceptionTableLength)
		for i := 0; i < len(self.exceptionTableList); i++ {
			self.exceptionTableList[i] = NewExceptionTable(&self.attributeData, cp)
		}
	}

	self.attributeCount = self.attributeData.ReadUInt16()
	if self.attributeCount > 0 {
		self.attributeInfoList = make([]AttributeInfo, self.attributeCount)
		for i := 0; i < int(self.attributeCount); i++ {
			self.attributeInfoList[i] = NewAttributeInfoFromBytes(&self.attributeData, cp)
		}
	}
	return self
}

func (self *CodeAttr) MaxStack() uint16 {
	return self.maxStack
}

func (self *CodeAttr) MaxLocals() uint16 {
	return self.maxLocals
}

func (self *CodeAttr) Code() []uint8 {
	return self.codeList
}

func (self *CodeAttr) LineNumberTableAttribute() *LineNumberTableAttr {
	for _, attrInfo := range self.attributeInfoList {
		switch attrInfo.(type) {
		case *LineNumberTableAttr:
			return attrInfo.(*LineNumberTableAttr)
		}
	}
	return nil
}

func (self *CodeAttr) ExceptionTable() []ExceptionTable {
	return self.exceptionTableList
}

func NewAttributeInfoFromBytes(cr *AttributeData, cp ConstantPool) AttributeInfo {
	attr := &AttrStruct{}
	attr.nameIndex = cr.ReadUInt16()
	attr.length = cr.ReadUInt32()
	attr.attributeName = cp.GetConstant(attr.nameIndex).(*Constant_Utf8)
	attr.attributeData = AttributeData{
		(&ClassReader{}).SetData(cr.ReadBytes(attr.length)),
	}
	return attr.readAttr(cp)
}

func NewExceptionTable(data *AttributeData, cp ConstantPool) ExceptionTable {
	e := ExceptionTable{}
	e.startPc = data.ReadUInt16()
	e.endPc = data.ReadUInt16()
	e.handlerPc = data.ReadUInt16()
	e.catchType = data.ReadUInt16()
	return e
}
